#include "helper.h"
#include "monitor.h"
#include "reg.h"

#define SRC_SIGN_MASK 0x80000000	// 源寄存器中操作数的符号位掩码
#define CPY_SRC_SIGN(src) (src |= ((op_## src->val & SRC_SIGN_MASK) << 1))	// 将32位数的符号位复制到第33位
#define CHECK_TMP_OVERFLOW(tmp) ((tmp & 0x100000000) != (tmp & 0x80000000))	// 检测tmp是否溢出
#define REG_MASK 0xFFFFFFFF			// 32位寄存器掩码

extern uint32_t instr;
extern char assembly[80];

/* decode R-type instrucion */
static void decode_r_type(uint32_t instr) {

	op_src1->type = OP_TYPE_REG;
	op_src1->reg = (instr & RS_MASK) >> (RT_SIZE + IMM_SIZE);
	op_src1->val = reg_w(op_src1->reg);
	
	op_src2->type = OP_TYPE_REG;
	op_src2->imm = (instr & RT_MASK) >> (RD_SIZE + SHAMT_SIZE + FUNC_SIZE);
	op_src2->val = reg_w(op_src2->reg);

	op_dest->type = OP_TYPE_REG;
	op_dest->reg = (instr & RD_MASK) >> (SHAMT_SIZE + FUNC_SIZE);
}

make_helper(add) {

	decode_r_type(instr);
	uint64_t src1 = op_src1->val, src2 = op_src2->val, tmp;
	CPY_SRC_SIGN(src1);
	CPY_SRC_SIGN(src2);
	tmp = src1 + src2;
	if(CHECK_TMP_OVERFLOW(tmp)){
		// Overflow
	}
	else reg_w(op_dest->reg) = (tmp & REG_MASK);
	sprintf(assembly, "add   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(addu) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = ((op_src1->val) + (op_src2->val));
	sprintf(assembly, "addu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sub) {

	decode_r_type(instr);
	uint64_t src1 = op_src1->val, src2 = op_src2->val, tmp;
	CPY_SRC_SIGN(src1);
	CPY_SRC_SIGN(src2);
	tmp = src1 - src2;
	if(CHECK_TMP_OVERFLOW(tmp)){
		// Overflow
	}
	else reg_w(op_dest->reg) = (tmp & REG_MASK);
	sprintf(assembly, "sub   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(subu) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = ((op_src1->val) - (op_src2->val));
	sprintf(assembly, "subu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(slt) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = ((op_src1->val) < (op_src2->val)) ? 1 : 0;
	sprintf(assembly, "slt   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sltu) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = ((unsigned)(op_src1->val) < (unsigned)(op_src2->val)) ? 1 : 0;
	sprintf(assembly, "sltu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(div) {

	decode_r_type(instr);
	cpu.lo = (op_src1->val / op_src2->val);
	cpu.hi = (op_src1->val % op_src2->val);
	sprintf(assembly, "div   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg));
}

make_helper(divu) {

	decode_r_type(instr);
	cpu.lo = ((unsigned)(op_src1->val) / (unsigned)(op_src2->val));
	cpu.hi = ((unsigned)(op_src1->val) % (unsigned)(op_src2->val));
	sprintf(assembly, "divu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(mult) {

	decode_r_type(instr);
	long prod = (op_src1->val * op_src2->val);
	cpu.lo = (prod);
	cpu.hi = (prod >> 8);
	sprintf(assembly, "mult   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(multu) {

	decode_r_type(instr);
	long prod = ((unsigned)(op_src1->val) * (unsigned)(op_src2->val));
	cpu.lo = (prod);
	cpu.hi = (prod >> 8);
	sprintf(assembly, "multu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(and) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val & op_src2->val);
	sprintf(assembly, "and   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(nor) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = ~(op_src1->val | op_src2->val);
	sprintf(assembly, "nor   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(or) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val | op_src2->val);
	sprintf(assembly, "or   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));

}

make_helper(xor) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val ^ op_src2->val);
	sprintf(assembly, "xor   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sllv) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src2->val << op_src1->val);
	sprintf(assembly, "sllv   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sll) {

	decode_r_type(instr);
	uint32_t sa = (instr & SHAMT_MASK) >> (FUNC_SIZE);
	reg_w(op_dest->reg) = (op_src2->val << sa);
	sprintf(assembly, "sll   %s,   %s,   sa", REG_NAME(op_dest->reg), REG_NAME(op_src2->reg));
}

make_helper(srav) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src2->val >> op_src1->val);
	sprintf(assembly, "srav   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sra) {

	decode_r_type(instr);
	uint32_t sa = (instr & SHAMT_MASK) >> (FUNC_SIZE);
	reg_w(op_dest->reg) = (op_src2->val >> sa);
	sprintf(assembly, "sra   %s,   %s,   sa", REG_NAME(op_dest->reg), REG_NAME(op_src2->reg));
}

make_helper(srlv) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = ((unsigned)(op_src2->val) >> op_src1->val);
	sprintf(assembly, "srlv   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(srl) {

	decode_r_type(instr);
	uint32_t sa = (instr & SHAMT_MASK) >> (FUNC_SIZE);
	reg_w(op_dest->reg) = ((unsigned)(op_src2->val) >> sa);
	sprintf(assembly, "srl   %s,   %s,   sa", REG_NAME(op_dest->reg), REG_NAME(op_src2->reg));
}

make_helper(beq) {

	decode_r_type(instr);
	uint32_t offset = (instr & IMM_MASK) << 2;
	if(op_src1->val == op_src2->val) cpu.pc += offset;
	sprintf(assembly, "beq   %s,   %s,   %d", REG_NAME(op_src1->reg), REG_NAME(op_src2->reg), offset);
}

make_helper(bne) {

	decode_r_type(instr);
	uint32_t offset = (instr & IMM_MASK) << 2;
	if(op_src1->val != op_src2->val) cpu.pc += offset;
	sprintf(assembly, "bne   %s,   %s,   %d", REG_NAME(op_src1->reg), REG_NAME(op_src2->reg), offset);
}

make_helper(bgez) {

	decode_r_type(instr);
	uint32_t offset = (instr & IMM_MASK) << 2;
	if(op_src1->val >= 0) cpu.pc += offset;
	sprintf(assembly, "bgez   %s,   %d", REG_NAME(op_src1->reg), offset);
}

make_helper(bgtz) {

	decode_r_type(instr);
	uint32_t offset = (instr & IMM_MASK) << 2;
	if(op_src1->val > 0) cpu.pc += offset;
	sprintf(assembly, "bgtz   %s,   %d", REG_NAME(op_src1->reg), offset);
}

make_helper(blez) {

	decode_r_type(instr);
	uint32_t offset = (instr & IMM_MASK) << 2;
	if(op_src1->val <= 0) cpu.pc += offset;
	sprintf(assembly, "blez   %s,   %d", REG_NAME(op_src1->reg), offset);
}

make_helper(bltz) {

	decode_r_type(instr);
	uint32_t offset = (instr & IMM_MASK) << 2;
	if(op_src1->val < 0) cpu.pc += offset;
	sprintf(assembly, "bltz   %s,   %d", REG_NAME(op_src1->reg), offset);
}

make_helper(bgezal) {

	decode_r_type(instr);
	uint32_t offset = (instr & IMM_MASK) << 2;
	cpu.ra = cpu.pc + 8;
	if(op_src1->val >= 0) cpu.pc += offset;
	sprintf(assembly, "bgezal   %s,   %d", REG_NAME(op_src1->reg), offset);
}

make_helper(bltzal) {

	decode_r_type(instr);
	uint32_t offset = (instr & IMM_MASK) << 2;
	cpu.ra = cpu.pc + 8;
	if(op_src1->val < 0) cpu.pc += offset;
	sprintf(assembly, "bltzal   %s,   %d", REG_NAME(op_src1->reg), offset);
}

make_helper(bz) {
	
	decode_r_type(instr);
	switch(op_src2->val){
		case 0x00000: bltz(pc); break;
		case 0x00001: bgez(pc); break;
		case 0x10001: bgezal(pc); break;
		case 0x10000: bltzal(pc); break;
	}
}

make_helper(jr) {

	decode_r_type(instr);
	cpu.pc = op_src1->val;
	sprintf(assembly, "jr   %s", REG_NAME(op_src1->reg));
}

make_helper(jalr) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = cpu.pc + 8;
	cpu.pc = op_src1->val;
	sprintf(assembly, "bltzal   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg));
}

make_helper(mfhi) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = cpu.hi;
	sprintf(assembly, "mfhi   %s", REG_NAME(op_dest->reg));
}

make_helper(mflo) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = cpu.lo;
	sprintf(assembly, "mflo   %s", REG_NAME(op_dest->reg));
}

make_helper(mthi) {

	decode_r_type(instr);
	cpu.hi = op_src1->val;
	sprintf(assembly, "mthi   %s", REG_NAME(op_src1->reg));
}

make_helper(mtlo) {

	decode_r_type(instr);
	cpu.lo = op_src1->val;
	sprintf(assembly, "mtlo   %s", REG_NAME(op_src1->reg));
}
